#include "GeometryHelper.h"
#include <math.h>
#include <float.h>

GeometryHelper * GeometryHelper::instance = 0;


bool GeometryHelper::isInfinity(float n) {
	if(n != n || n >= DBL_MAX || n <= -DBL_MAX) {
		return true;
	}
	return false;
}

int GeometryHelper::sign(int value) {
	if(value > 0) {
		return 1;
	}
	if(value < 0){
		return -1;
	}else {
		return 1;
	}
}

GeometryHelper::GeometryHelper(void){
}

GeometryHelper::~GeometryHelper(void){
}

GeometryHelper * GeometryHelper::getInstance() {
	if (!instance){
		instance = new GeometryHelper();
	}
	return instance;
}

float GeometryHelper::correctAngle( float angle, int signum ){
	float m = tan(angle);
	return getAngle(m,signum);
}

float GeometryHelper::getAngle( float m, int signum ){
	float angle = 0;
	float PI = 3.14159;
	if(m == 0) {
		if(signum > 0) {
			angle = 0;
		}else if(signum < 0) {
			angle = PI;
		}		
	}else {
		if(signum > 0) {
			angle = PI + atan(m);
		}else if(signum < 0) {
			angle = atan(m);
		}
	}
	return angle;
}

float GeometryHelper::getAngle( Position * p1, Position * p2 )
{
	float dx = p2->getPixelX() - p1->getPixelX();
	float dy = p2->getPixelY() - p1->getPixelY();
	return atan2(dy,dx);
}

float GeometryHelper::getAngle( Position * p1, Position * p2, int signum){

	float dx = p2->getPixelX() - p1->getPixelX();
	float dy = p2->getPixelY() - p1->getPixelY();
	return getAngle((dy/dx), signum);
}

Position * GeometryHelper::getPosition( float distance, float m, Position * origin ){
	Position * pos = new Position();
	pos->setPixelX(origin->getPixelX()+distance*m);
	pos->setPixelY(origin->getPixelY()+distance*m);

	return pos;
}

float GeometryHelper::calculateDistance( Position * originPoint, Position * endPoint ){
	float distanceX2 = pow((float)(originPoint->getPixelX() - endPoint->getPixelX()),2);
	float distanceY2 = pow((float)(originPoint->getPixelY() - endPoint->getPixelY()),2);
	float distance = pow(distanceX2 + distanceY2, 0.5f);

	return distance;
}

Position * GeometryHelper::getNearerPoint( Position * point, float xb, float m, float b ){
	float PI = 3.14159;
	float angle = atan(m);
	float normalAngle = angle + PI / 2;
	float mNormal = tan(normalAngle);

	Position * nearerPoint = this->getIntersection(xb,m,b,point->getPixelX(),mNormal,point->getPixelY());
	return nearerPoint;
}

float GeometryHelper::fx( float x, float m, float b ){
	return x*m + b;
}

float GeometryHelper::getXIntersection(float m1, float b1, float m2, float b2 ){
	float xi = 0;
	if(m2 != m1) {
		xi = (float) (b1 - b2)/(m2 - m1);
	}else {
		throw ParallelStraightsException("Las rectas son paralelas.");
	}	
	return xi;
}

Position * GeometryHelper::getIntersection( float xb, float m, float b, float xb2, float m2, float b2 ){
	float xIntersection = DBL_MAX, yIntersection = DBL_MAX ;
	float xI;
	if(isInfinity(m) || isInfinity(m2)) {
		if(isInfinity(m)) {
			xIntersection = xb;
			yIntersection = fx(xIntersection-xb2, m2, b2);
		}
		if(isInfinity(m2)) {
			if(isInfinity(m) && (xb2 == xIntersection || xb2 != xIntersection)) {
				// Hacer mas robusto, caso que sean la misma recta o sean paralelas.
				return NULL;
			}
			xIntersection = xb2;
			yIntersection = fx(xIntersection-xb, m, b);
		}
	} else {
		xI = this->getXIntersection(m,fx(xb2-xb,m,b),m2,b2);
		xIntersection = xb2 + xI;
		yIntersection = fx(xI, m2, b2);
	}	
	Position * intersectionPoint = new Position();
	intersectionPoint->setPixel(xIntersection,yIntersection);
	return intersectionPoint;
}

